import weakref, random
import pyglet
from vec2d import Vec2d

import resource
from fastsprite import Sprite
from util import Rectangle

# global used to give unique ID #s to each robot.
robot_count = 0

class RobotScrap(object):
    def __init__(self, scrap_sprite, x, y):
        self.sprite = scrap_sprite

        self.position = Vec2d(x, y)
        self.shape = Rectangle(-10,-5, 20, 20)
        
        self.map = None

        self.timer = 6

    def draw(self):
        self.sprite.position = self.position
        self.sprite.draw()

    def update(self, dt):
        self.timer -= dt
        if self.timer <= 0:
            self.map.remove_object(self)

    def rectangle(self):
        return Rectangle(self.position.x + self.shape.x, self.position.y + self.shape.y, self.shape.width, self.shape.height)

######################################################################

class Robot(object):
    """Base class for all robots to inherit from."""
    robot_death_sound = pyglet.resource.media('sounds/robot_death_2.wav', streaming=False)

    def __init__(self):
        self.map = None
        self.hit_points = 10
        self.state = 'normal'
        self.state_timer = 0

        # AI stuff.
        self.goal = None
        self.path_index = 0

        self.normal_speed = 100   # pixels per second
        self.speed = self.normal_speed
        
        global robot_count
        self.id = robot_count
        robot_count += 1

        # Derived class needs to:
        # 1. Set up sprites
        # 2. Set up collision shape
        # 3. Set position
        # 4. Change hit_points and normal_speed if desired.
        # 5. Set other per-class stats.

    def rectangle(self):
        return Rectangle(self.position.x + self.shape.x, self.position.y + self.shape.y, self.shape.width, self.shape.height)

    def draw(self):
        self.sprite.position = self.position
        self.sprite.draw()

    def update(self, dt):
        """Override in derived class."""
        pass

    def follow_path(self, dt):
        # If robot's state is normal, and it has a current goal position,
        # then move towards the goal.
        if self.goal:
            # Compute delta, a unit vector in direction that robot is traveling.
            delta = self.goal - self.position
            distance = delta.normalize_return_length()

            if delta:
                self.position += self.speed*dt*delta

            if distance < 10:
                # Reached to goal point, so move on to next one.
                self.path_index += 1
                if self.path_index < len(self.map.robot_waypoints):
                    self.goal = self.map.robot_waypoints[self.path_index]
                else:
                    # reached end of path. delete this robot?
                    self.map.remove_object(self)                    

    def take_damage(self, damage):
        """Decrease robot's hit points and change its state to show that it has been hit."""
        self.hit_points -= damage
        self.state = 'hit'
        self.state_timer = 0.2
        if hasattr(self, 'hit_sprite'):
            self.sprite = self.hit_sprite

    def slow_down(self, factor):
        """Slow the robot's speed by some factor (a number between 0 and 1)"""
        self.speed = self.normal_speed * factor
        self.state = 'glued'
        self.state_timer = 5
        if hasattr(self, 'glued_sprite'):
            self.sprite = self.glued_sprite

######################################################################

class RedRobot(Robot):

    def __init__(self, x, y):
        Robot.__init__(self)

        self.normal_sprite = Sprite(resource.texture('graphics/robots/red_robot.png', 16, 5))
        self.hit_sprite = Sprite(resource.texture('graphics/robots/red_robot_hit.png', 22, 5))
        self.glued_sprite = Sprite(resource.texture('graphics/robots/red_robot_glued.png', 17, 5))
        self.damaged_sprite = Sprite(resource.texture('graphics/robots/red_robot_damaged.png', 16, 5))        
        self.scrap_sprite = self.sprite = Sprite(resource.texture('graphics/robots/red_robot_scrap.png', 16, 5))
        self.sprite = self.normal_sprite

        self.position = Vec2d(x, y)
        self.shape = Rectangle(-10,-5, 20, 15)

    def update(self, dt):

        if self.state == 'glued':
            self.state_timer -= dt
            if self.state_timer <= 0:
                self.state = 'normal'
                self.sprite = self.normal_sprite
                self.speed = self.normal_speed

        if self.state == 'hit':
            self.state_timer -= dt
            if self.state_timer <= 0: 
                # After hit animation finishes, check to see if this robot is dead.
                # If it is, remove it from the map, otherwise change its state back to normal.
                if self.hit_points <= 0:
                    self.robot_death_sound.play()
                    self.map.remove_object(self)
                    self.map.add_object(RobotScrap(self.scrap_sprite, self.position.x, self.position.y))
                else:
                    self.state = 'normal'
                    self.speed = self.normal_speed
                    self.sprite = self.damaged_sprite

        self.follow_path(dt)
######################################################################

class GreenRobot(Robot):

    def __init__(self, x, y):
        Robot.__init__(self)

        self.normal_sprite = Sprite(resource.texture('graphics/robots/green_robot.png', 16, 5))
        self.hit_sprite = Sprite(resource.texture('graphics/robots/green_robot_hit.png', 22, 5))
        self.glued_sprite = Sprite(resource.texture('graphics/robots/green_robot_glued.png', 17, 5))
        self.damaged_sprite = Sprite(resource.texture('graphics/robots/green_robot_damaged.png', 16, 5))        
        self.scrap_sprite = self.sprite = Sprite(resource.texture('graphics/robots/green_robot_scrap.png', 16, 5))
        self.sprite = self.normal_sprite

        self.position = Vec2d(x, y)
        self.shape = Rectangle(-10,-5, 20, 15)

    def update(self, dt):

        if self.state == 'glued':
            self.state_timer -= dt
            if self.state_timer <= 0:
                self.state = 'normal'
                self.sprite = self.normal_sprite
                self.speed = self.normal_speed

        if self.state == 'hit':
            self.state_timer -= dt
            if self.state_timer <= 0: 
                # After hit animation finishes, check to see if this robot is dead.
                # If it is, remove it from the map, otherwise change its state back to normal.
                if self.hit_points <= 0:
                    self.robot_death_sound.play()
                    self.map.remove_object(self)
                    self.map.add_object(RobotScrap(self.scrap_sprite, self.position.x, self.position.y))
                else:
                    self.state = 'normal'
                    self.speed = self.normal_speed
                    self.sprite = self.damaged_sprite

        self.follow_path(dt)

######################################################################

class ArmorRobot(Robot):

    def __init__(self, x, y):
        Robot.__init__(self)

        self.normal_sprite = Sprite(resource.texture('graphics/robots/armor_robot.png', 16, 5))
        self.hit_sprite = Sprite(resource.texture('graphics/robots/armor_robot_hit.png', 22, 5))
        self.glued_sprite = Sprite(resource.texture('graphics/robots/armor_robot_glued.png', 17, 5))
        self.damaged_sprite = Sprite(resource.texture('graphics/robots/armor_robot_damaged.png', 16, 5))        
        self.scrap_sprite = self.sprite = Sprite(resource.texture('graphics/robots/armor_robot_scrap.png', 16, 5))
        self.sprite = self.normal_sprite

        self.position = Vec2d(x, y)
        self.shape = Rectangle(-10,-5, 20, 15)

    def update(self, dt):

        if self.state == 'glued':
            self.state_timer -= dt
            if self.state_timer <= 0:
                self.state = 'normal'
                self.sprite = self.normal_sprite
                self.speed = self.normal_speed

        if self.state == 'hit':
            self.state_timer -= dt
            if self.state_timer <= 0: 
                # After hit animation finishes, check to see if this robot is dead.
                # If it is, remove it from the map, otherwise change its state back to normal.
                if self.hit_points <= 0:
                    self.robot_death_sound.play()
                    self.map.remove_object(self)
                    self.map.add_object(RobotScrap(self.scrap_sprite, self.position.x, self.position.y))
                else:
                    self.state = 'normal'
                    self.speed = self.normal_speed
                    self.sprite = self.damaged_sprite

        self.follow_path(dt)

######################################################################

class PurpleRobot(Robot):

    def __init__(self, x, y):
        Robot.__init__(self)

        self.normal_sprite = Sprite(resource.texture('graphics/robots/purple_robot.png', 16, 5))
        self.hit_sprite = Sprite(resource.texture('graphics/robots/purple_robot_hit.png', 22, 5))
        self.glued_sprite = Sprite(resource.texture('graphics/robots/purple_robot_glued.png', 17, 5))
        self.damaged_sprite = Sprite(resource.texture('graphics/robots/purple_robot_damaged.png', 16, 5))        
        self.scrap_sprite = self.sprite = Sprite(resource.texture('graphics/robots/purple_robot_scrap.png', 16, 5))
        self.sprite = self.normal_sprite

        self.position = Vec2d(x, y)
        self.shape = Rectangle(-10,-5, 20, 15)

    def update(self, dt):

        if self.state == 'glued':
            self.state_timer -= dt
            if self.state_timer <= 0:
                self.state = 'normal'
                self.sprite = self.normal_sprite
                self.speed = self.normal_speed

        if self.state == 'hit':
            self.state_timer -= dt
            if self.state_timer <= 0: 
                # After hit animation finishes, check to see if this robot is dead.
                # If it is, remove it from the map, otherwise change its state back to normal.
                if self.hit_points <= 0:
                    self.robot_death_sound.play()
                    self.map.remove_object(self)
                    self.map.add_object(RobotScrap(self.scrap_sprite, self.position.x, self.position.y))
                else:
                    self.state = 'normal'
                    self.speed = self.normal_speed
                    self.sprite = self.damaged_sprite

        self.follow_path(dt)

######################################################################

class YellowRobot(Robot):

    def __init__(self, x, y):
        Robot.__init__(self)

        self.normal_sprite = Sprite(resource.texture('graphics/robots/yellow_robot.png', 16, 5))
        self.hit_sprite = Sprite(resource.texture('graphics/robots/yellow_robot_hit.png', 22, 5))
        self.glued_sprite = Sprite(resource.texture('graphics/robots/yellow_robot_glued.png', 17, 5))
        self.damaged_sprite = Sprite(resource.texture('graphics/robots/yellow_robot_damaged.png', 16, 5))        
        self.dodge_sprite = Sprite(resource.texture('graphics/robots/yellow_robot_dodge.png', 16, 5))        
        self.scrap_sprite = Sprite(resource.texture('graphics/robots/yellow_robot_scrap.png', 16, 5))        
        self.sprite = self.normal_sprite

        self.position = Vec2d(x, y)
        self.shape = Rectangle(-10,-5, 20, 15)

    def update(self, dt):

        if self.state == 'glued':
            self.state_timer -= dt
            if self.state_timer <= 0:
                self.state = 'normal'
                self.sprite = self.normal_sprite
                self.speed = self.normal_speed

        if self.state == 'dodge':
            self.state_timer -= dt
            if self.state_timer <= 0:
                self.state = 'normal'
                self.sprite = self.normal_sprite

        if self.state == 'hit':
            self.state_timer -= dt
            if self.state_timer <= 0: 
                # After hit animation finishes, check to see if this robot is dead.
                # If it is, remove it from the map, otherwise change its state back to normal.
                if self.hit_points <= 0:
                    self.robot_death_sound.play()
                    self.map.remove_object(self)
                    self.map.add_object(RobotScrap(self.scrap_sprite, self.position.x, self.position.y))
                else:
                    self.state = 'normal'
                    self.speed = self.normal_speed
                    self.sprite = self.damaged_sprite

        self.follow_path(dt)

    # Override the yellow robot's take_damage method so that it
    # can occasionally dodge attacks.
    def take_damage(self, damage):
        """Decrease robot's hit points and change its state to show that it has been hit."""
        if random.randint(1,3) == 3:
            self.state = 'dodge'
            self.state_timer = 0.2
            if hasattr(self, 'dodge_sprite'):
                self.sprite = self.dodge_sprite
        else:
            self.hit_points -= damage
            self.state = 'hit'
            self.state_timer = 0.2
            if hasattr(self, 'hit_sprite'):
                self.sprite = self.hit_sprite
        
